/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-12-10     xu       the first version
 */
#include "ui_task.h"
#include "EPD_4in2b_V2.h"
#include "GUI_Paint.h"
#include "message_type.h"
#include "ui_draw.h"

#define DBG_TAG "ui"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#endif

#define UI_TASK_TICKS 1
#define UI_TASK_PRIORITY 16
#define UI_TASK_STACK_SIZE 8192
#define UI_MQ_MAX_MSGS 128

static void ui_entry(void* param)
{
    rt_err_t err;
    ui_task_t context = (ui_task_t)param;
    if (context == RT_NULL) {
        LOG_E("%s: context null!", __func__);
        return;
    }

    RT_ASSERT(context->mq);
    LOG_I("ui task work start...");

    struct message* message = rt_malloc(sizeof(struct message));
    if (message == RT_NULL) {
        LOG_E("%s: alloc message failed!", __func__);
        return;
    }

    EPD_4IN2B_V2_Init();
    err = rt_sem_release(context->ready);
    if (err != RT_EOK) {
        LOG_E("notify ready failed!");
        return;
    }

    while (context->running) {
        err = rt_mq_recv(context->mq, message, sizeof(*message), rt_tick_from_millisecond(10 * 1000));
        if (err == -RT_ETIMEOUT) {
            // LOG_D("%s: recv message timeout, continue %d ...", context->name, rt_tick_get_millisecond());
            continue;
        } else if (err != RT_EOK) {
            LOG_E("%s: recv message failed!", context->name);
            break;
        }
        if (message->type != MSG_VIEW_COMMAND) {
            continue;
        }
        struct view_command cmd = message->data.view_command;
        context->drawing = RT_TRUE;
        switch (cmd.view) {
            case VIEW_MONTHLY:
                draw_month_view();
                break;
            case VIEW_DAILY:
                draw_daily_view();
                break;
            default:
                break;
        }
        context->drawing = RT_FALSE;
    }

    rt_free(message);
    err = rt_sem_release(context->finish);
    if (err != RT_EOK) {
        LOG_E("notiify ui task finish to semaphore failed!");
    }
    LOG_I("ui task work done...");
}

ui_task_t ui_task_create()
{
    ui_task_t context = rt_malloc(sizeof(struct ui_task));
    if (context == RT_NULL) {
        LOG_E("%s: alloc context failed!", __func__);
        return RT_NULL;
    }

    const char* name = "ui";
    rt_mq_t mq = rt_mq_create(name, sizeof(struct message), UI_MQ_MAX_MSGS, RT_IPC_FLAG_FIFO);
    if (mq == RT_NULL) {
        LOG_E("mq null!", mq);
        rt_free(context);
        return RT_NULL;
    }

    rt_sem_t ready = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO);
    if (ready == RT_NULL) {
        LOG_E("create sem %s failed!", name);
        rt_free(context);
        rt_mq_delete(mq);
        return RT_NULL;
    }

    rt_sem_t finish = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO);
    if (finish == RT_NULL) {
        LOG_E("create sem %s failed!", name);
        rt_free(context);
        rt_mq_delete(mq);
        rt_sem_delete(ready);
        return RT_NULL;
    }

    rt_thread_t thread = rt_thread_create(name, ui_entry, context, UI_TASK_STACK_SIZE, UI_TASK_PRIORITY, UI_TASK_TICKS);
    if (thread == RT_NULL) {
        LOG_E("create thread %s failed!", name);
        rt_free(context);
        rt_mq_delete(mq);
        rt_sem_delete(ready);
        rt_sem_delete(finish);
        return RT_NULL;
    }

    context->name = name;
    context->running = RT_TRUE;
    context->drawing = RT_FALSE;
    context->mq = mq;
    context->ready = ready;
    context->finish = finish;
    context->thread = thread;
    LOG_I("ui task create done!");
    return context;
}

rt_bool_t ui_task_start(ui_task_t context)
{
    rt_err_t err;
    if (context == RT_NULL) {
        LOG_E("%s: context null!", __func__);
        return RT_FALSE;
    }

    err = rt_thread_startup(context->thread);
    if (err != RT_EOK) {
        LOG_E("start thread %s failed!", context->name);
        return RT_FALSE;
    }

    err = rt_sem_take(context->ready, RT_WAITING_FOREVER);
    if (err != RT_EOK) {
        LOG_E("wait %s task ready failed!", context->name);
        return RT_FALSE;
    }

    LOG_I("ui task start done!");
    return RT_TRUE;
}

rt_bool_t ui_task_stop(ui_task_t context)
{
    rt_err_t err;
    if (context == RT_NULL) {
        LOG_E("%s: context null!", __func__);
        return RT_FALSE;
    }

    context->running = RT_FALSE;
    rt_thread_yield();

    err = rt_sem_take(context->finish, RT_WAITING_FOREVER);
    if (err != RT_EOK) {
        LOG_E("wait %s task stop failed!", context->name);
        return RT_FALSE;
    }

    LOG_I("ui task stop done!");
    return RT_TRUE;
}

rt_bool_t ui_task_destroy(ui_task_t context)
{
    rt_err_t err = 0;
    if (context == RT_NULL) {
        LOG_E("%s: context null!", __func__);
        return RT_FALSE;
    }

    err |= rt_thread_delete(context->thread);
    err |= rt_mq_delete(context->mq);
    err |= rt_sem_delete(context->ready);
    err |= rt_sem_delete(context->finish);
    rt_free(context);
    if (err != RT_EOK) {
        LOG_E("%s: some error raised!", __func__);
        return RT_FALSE;
    }
    LOG_I("ui task destroy done!");
    return RT_TRUE;
}

rt_bool_t ui_task_control(ui_task_t context, rt_uint8_t cmd)
{
    if (context == RT_NULL) {
        LOG_E("%s: context null!", __func__);
        return RT_FALSE;
    }

    LOG_I("%s: cmd = %d", __func__, cmd);
    if (context->drawing) {
        LOG_W("E-Paper device busy, cmd ignored!");
        return RT_TRUE;
    }

    struct message message = {0};
    message.type = MSG_VIEW_COMMAND;
    message.data.view_command.view = cmd;
    rt_err_t err = rt_mq_send(context->mq, &message, sizeof(message));
    if (err != RT_EOK) {
        LOG_E("%s: send message to mq failed!");
        return RT_FALSE;
    }
    return RT_TRUE;
}

rt_bool_t ui_draw_month_view(ui_task_t context)
{
    return ui_task_control(context, VIEW_MONTHLY);
}

rt_bool_t ui_draw_daily_view(ui_task_t context)
{
    return ui_task_control(context, VIEW_DAILY);
}

static int ui_test(int argc, char* argv[])
{
    if (argc < 2) {
        rt_kprintf("Usage: %s cmd\n", __func__);
        return -1;
    }

    uint8_t cmd = atoi(argv[1]);
    if (cmd > VIEW_DAILY) {
        rt_kprintf("invalid cmd: %d\n", cmd);
        return -1;
    }

    ui_task_t task = ui_task_create();
    if (task == RT_NULL) {
        rt_kprintf("ui task create failed!");
        return -1;
    }

    rt_kprintf("start ui task...\n");
    if (ui_task_start(task) != RT_TRUE) {
        rt_kprintf("ui task start failed!");
        return -1;
    }

    if (ui_task_control(task, cmd) != RT_TRUE) {
        rt_kprintf("ui task control failed!");
        return -1;
    }

    rt_kprintf("stop ui task...\n");
    if (ui_task_stop(task) != RT_TRUE) {
        rt_kprintf("ui task stop failed!");
        return -1;
    }

    if (ui_task_destroy(task) != RT_TRUE) {
        rt_kprintf("ui task destroy failed!");
        return -1;
    }
    return 0;
}
MSH_CMD_EXPORT(ui_test, UI test);